<?php
# test stub for handling events
class Test_Event_Event_Stub_Handler extends Event_Handler {
	protected function onExpectException(Event_Event $oEvent) {
		throw new Exception('triggered!', 1);
	}
	protected function onDontPreventDefault(Event_Event $oEvent) {}
	protected function onPreventDefault(Event_Event $oEvent) {
		$oEvent->preventDefault();
	}
	protected function onPassData(Event_Event $oEvent) {
		$oEvent->setData('is_set', 'yes');
		$oEvent->setData(15, true);
	}
}
# triggerer provides a method (__construct) that can take be called with a variety of parameters
class Test_Event_Event_Stub_Triggerer {
	public static function staticTriggerer() {
		return Event_Event::trigger('statically-triggered');
	}
	public static function trigger($bHandle = false) {
		return new Event_Event(($bHandle?'handle-':'').'static-source');
	}
	public $oEvent;
	public function __construct() {
		$this->oEvent = new Event_Event('test-provide-args');
	}
}

class Test_Event_Event extends PHPUnit_Framework_TestCase {
	/**
	 * Creating an Event should trigger the corresponding handler
	 *
	 * Expected behaviour: constructing an event triggers its handlers
	 *
	 * Test setup:
	 * use the stub handler to listen to events. this returns an exception which signals the
	 * event reached the handler
	 */
	public function test_constructing_event_triggers_handling() { // __construct
		new Test_Event_Event_Stub_Handler('Test_Event_Event');
		try {
			new Event_Event('expect-exception');
			$this->fail('triggering proper response should have thrown');
		} catch (Exception $oEx) {
			if (false === ($oEx->getCode() === 1 && $oEx->getMessage() === 'triggered!')) {
				$this->fail('improper exception received');
			}
		}
	}

	/**
	 * tests trigger(..)
	 * the static way to trigger events
	 *
	 * test setup:
	 * statically trigger a number of events, increasinly providing more arguments.
	 * inspect the resulting event and check that the arguments match.
	 */
	public function testEventsCanBeStaticallyTriggered() {
		$oEvent = Event_Event::trigger('event-name');
		$this->assertTrue($oEvent instanceof Event_Event, 'the returned value should be an Event');
		$this->assertEquals('event-name', $oEvent->getType(), 'the event type was not used');

		$asParam = array('param' => 'value');
		$oEvent = Event_Event::trigger('event-name', $asParam);
		$this->assertEquals($asParam, $oEvent->getParams($asParam), 'the event type was not used');
		$this->assertFalse($oEvent->isDefaultPrevented(), 'the event should not flag prevention of default');

		$oEvent = Test_Event_Event_Stub_Triggerer::staticTriggerer('test-arg');
		$this->assertEquals('statically-triggered', $oEvent->getType(), 'wrong event type returned');
		$this->assertEquals('Test_Event_Event_Stub_Triggerer', $oEvent->getSource(), 'wrong source type');
		$this->assertEquals(array('test-arg'), $oEvent->getArgs(), 'wrong arguments');
	}


	/**
	 * Events carry an array of arguments that were provided to the method in
	 * which they were triggered
	 *
	 * Expected beahviour: Whatever params a triggering method receives, should be accessible through the Event
	 *
	 * Test setup:
	 * use the Test_Event_Event_Stub_Triggerer constructor with a variety of parameters and
	 * verify their presence in the event
	 */
	public function test_events_provide_args_of_triggering_method() { // getArgs
		$oStub = new Test_Event_Event_Stub_Triggerer();
		$this->assertEquals(array(), $oStub->oEvent->getArgs(), 'event args not right (empty)');
		$oStub = new Test_Event_Event_Stub_Triggerer(null);
		$this->assertEquals(array(null), $oStub->oEvent->getArgs(), 'event args not right (null)');
		$oStub = new Test_Event_Event_Stub_Triggerer('something');
		$this->assertEquals(array('something'), $oStub->oEvent->getArgs(), 'event args not right (single arg)');
		$oObj = new Event_Event('test-obj');
		$oStub = new Test_Event_Event_Stub_Triggerer('something', 'something-else', $oObj);
		$this->assertEquals(array('something', 'something-else', $oObj), $oStub->oEvent->getArgs(), 'event args not right (multiple mixed arg)');

		# test statical trigger
	}

	/**
	 * Events allow the object instance that triggered them to be reveiled
	 * Expected bahaviour:
	 * 1) called statically: source should be the name of the triggering class
	 * 2) called non-statically: source is the triggering instance
	 */
	public function test_events_source_is_accessible() { // getSource
		$oEvent = new Event_Event('test-source');
		$this->assertEquals($this, $oEvent->getSource(), 'Event source should be $this, but is not');

		$oStub = new Test_Event_Event_Stub_Triggerer();
		$this->assertEquals($oStub, $oStub->oEvent->getSource(), 'Event source should be $oStub, but is not');

		$oEvent = Test_Event_Event_Stub_Triggerer::trigger();
		$this->assertEquals('Test_Event_Event_Stub_Triggerer', $oEvent->getSource(), 'Event source should be "Test_Event_Event_Stub_Triggerer", but is not');
	}

	/**
	 * Events may be given additional parameters when triggered (constructed)
	 *
	 * Expected behaviour: whatever params the event was constructed with, should be returned,
	 * or an empty array when nothing was provided
	 */
	public function test_events_can_get_additional_params() { // getParams
		$amPossibleParams = array(
			array(),
			array('some param', 'param_key' => 'param value')
		);
		foreach ($amPossibleParams as $mPossibleParams) {
			$oEvent = new Event_Event('non-triggering-type', $mPossibleParams);
			$this->assertEquals($mPossibleParams, $oEvent->getParams(), 'event params not right ('.print_r($mPossibleParams, 1).')');
		}
		$oEvent = new Event_Event('non-triggering-type', null);
		$this->assertEquals(array(), $oEvent->getParams(), 'event params not right (null)');
		$oEvent = new Event_Event('non-triggering-type');
		$this->assertEquals(array(), $oEvent->getParams(), 'event params not right (empty)');
	}

	/**
	 * Events have a type that may be gotten
	 *
	 * Expected behaviour: whatever the type the event was constructed with, should be returned
	 */
	public function test_event_type_is_accessible() { // getType
		$asPossibleType = array(
			'multiple-type-type',
			'singletype',
			'singleType'
		);
		foreach ($asPossibleType as $sPossibleType) {
			$oEvent = new Event_Event($sPossibleType);
			$this->assertEquals($sPossibleType, $oEvent->getType(), 'event type not right ('.$sPossibleType.')');
		}
	}

	public function test_events_can_signal_triggerers_to_prevent_their_default_action() {
		new Test_Event_Event_Stub_Handler(__CLASS__);
		$oEvent = new Event_Event('prevent-default');
		$this->assertTrue($oEvent->isDefaultPrevented(), 'Default prevented should have been signalled');
		new Test_Event_Event_Stub_Handler(__CLASS__);
		$oEvent = new Event_Event('dont-prevent-default');
		$this->assertFalse($oEvent->isDefaultPrevented(), 'Default prevented should have been signalled');
	}

	/**
	 * Test whether setting and getting data in handler (set) and triggerer (get)
	 * works as expected.
	 */
	public function test_events_can_pass_data_back_to_triggerer() {
		new Test_Event_Event_Stub_Handler(__CLASS__);
		$oEvent = new Event_Event('pass-data');
		$this->assertEquals(
			array(
				'is_set' => 'yes',
				'15' => true,
			),
			$oEvent->getData(), 'Incorrect result from get ALL data'
		);
		$this->assertEquals(
			'yes', $oEvent->getData('is_set'), 'Incorrect result from get data'
		);
		$this->assertEquals(
			true, $oEvent->getData(15), 'Incorrect result from get 15 data'
		);
	}
}